#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include "stack.h"

typedef struct js_entry {
    struct js_entry *prev;
    struct js_entry *next;
    union {
        void    *ptr;
        uint64_t data;
    } v;
} js_entry_t;

struct js_stack_t {
    int size;
    struct js_entry *head;
    struct js_entry *tail;
};

#define JS_ASSERT(msg, expr) {\
    if (!(expr)) {\
        fprintf(stderr, "[%s:%d:%s] '%s' (%s)\n", \
                __FILE__, __LINE__, __func__, msg, #expr);\
        asm volatile("int3");\
    }\
}

#define STACK_EMPTY ((struct js_entry*)0x00200200)

int js_stack_size(js_stack_t *stack)
{
  return (stack->size);
}

js_stack_t *js_stack_new(void)
{
  js_stack_t *stack = (js_stack_t*) malloc(sizeof(*stack));
  stack->size = 0;
  stack->head = stack->tail = STACK_EMPTY;
  return stack;
}

void js_stack_push(js_stack_t *stack, void *value)
{
  js_entry_t *e;
  JS_ASSERT("stack is deleted", stack->size >= 0);
  e = (js_entry_t*) malloc(sizeof(*e));
  e->v.ptr = value;
  e->prev = stack->tail;
  e->next = STACK_EMPTY;
  if (stack->tail == STACK_EMPTY) {
      stack->head = e;
      stack->tail = e;
  } else {
      stack->tail->next = e;
      stack->tail = e;
  }
  stack->size++;
}

void *js_stack_pop(js_stack_t *stack)
{
  js_entry_t *e;
  void *value;
  JS_ASSERT("stack is deleted", stack->size >= 0);
  if (stack->size == 0) {
      fprintf(stderr, "stack is empty\n");
      exit(EXIT_FAILURE);
  }
  e = stack->tail;
  stack->tail = e->prev;
  value = e->v.ptr;
  free(e);
  stack->size--;
  return value;
}

void js_stack_delete(js_stack_t *stack)
{
  js_entry_t *e = stack->head;
  JS_ASSERT("stack is deleted", stack->size >= 0);
  while (e != STACK_EMPTY) {
      js_entry_t *old = e;
      free(old);
      e = e->next;
  }
  memset(stack, 0, sizeof(*stack));
  stack->size = -1;
  free(stack);
}

